﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Wicresoft.Shell.Core
{
    public class IntelliSenseManager
    {
        //local variable use for design
        public static Dictionary<string, UmcVariable> FullcontextVariable = new Dictionary<string, UmcVariable>();

        //local Variable use for editor
        private static Dictionary<string, UmcVariable> contextVariable = new Dictionary<string, UmcVariable>();

        public static Dictionary<string, UmcVariable> GetVariables()
        {
            return contextVariable.Concat(UmcReflector.GlobalVariables).ToDictionary(pair => pair.Key, pair => pair.Value);
        }


        /// <summary>
        /// parse the context code and try to find the local variable in #set statement and store them
        /// </summary>
        /// <param name="context"></param>
        public static void ParseContext(string context , bool design)
        {
            if (context != null)
            {
                if (design)
                {
                    FullcontextVariable.Clear();
                }
                else
                {
                    //clear the old list firstly
                    contextVariable.Clear();
                }

                /****  collect all varaibles in the above context  ****/
                string setStatementPattern = @"^[ \t]*#set[ \t]*\([ \t]*\$[^=\r\n]+=[^=\r\n]+\)";
                //match the pattern
                MatchCollection mc = Regex.Matches(context, setStatementPattern, RegexOptions.Multiline);
                foreach (Match m in mc)
                {
                    //predeal the set statement
                    string cutCurly = m.Value.Replace("{", "").Replace("}", "").Replace(" ", "");
                    if (CheckSetStatement(cutCurly))
                    {
                        //get the variable name
                        int LeftvNameStart = cutCurly.IndexOf('$') + 1;
                        int LeftvNameLength = cutCurly.IndexOf('=') - LeftvNameStart;
                        string LeftvName = cutCurly.Substring(LeftvNameStart, LeftvNameLength);
                        //get the variable right string
                        int RightReferenceStart = cutCurly.IndexOf('=') + 1;
                        int RightReferenceLength = cutCurly.Length - RightReferenceStart - 1;
                        //TODO : if right expression contains operation like #set($app=$app2.appe+1) , still take "$app2.appe+1"
                        string RightExpresion = cutCurly.Substring(RightReferenceStart, RightReferenceLength);

                        string v = string.Empty;

                        if (design)
                        {
                            //add the local variable 
                            if (FullcontextVariable.ContainsKey(LeftvName))
                            {
                                FullcontextVariable[LeftvName].VariableType = GetExpressionType(RightExpresion, ref v);
                                FullcontextVariable[LeftvName].VariableValue = v;
                            }
                            else
                            {
                                UmcVariable uv = new UmcVariable(LeftvName, GetExpressionType(RightExpresion, ref v));
                                uv.VariableValue = v;
                                FullcontextVariable.Add(LeftvName, uv);
                            }
                        }
                        else
                        {
                            //add the local variable
                            if (contextVariable.ContainsKey(LeftvName))
                            {
                                contextVariable[LeftvName].VariableType = GetExpressionType(RightExpresion, ref v);
                                contextVariable[LeftvName].VariableValue = v;
                            }
                            else
                            {
                                UmcVariable uv = new UmcVariable(LeftvName, GetExpressionType(RightExpresion, ref v));
                                uv.VariableValue = v;
                                contextVariable.Add(LeftvName, uv);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// the nest class which is a stack
        /// </summary>
        private class charStack
        {
            protected char[] stack;
            protected int top = -1;
            public charStack(int max)
            {
                stack = new char[max];
            }

            public void Push(char ch)
            {
                stack[++top] = ch;
            }

            public char Pop()
            {
                if (top < 0)
                {
                    throw new IndexOutOfRangeException();
                }
                else
                    return stack[top--];
            }

            public bool IsEmpty
            {
                get { return top < 0; }
            }

        }
        /// <summary>
        /// split operation using for split the right expression
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private static string[] ExpressionSplit(string expression)
        {
            string[] ret;
            if (expression == "")
            {
                ret = new string[0];
                return ret;
            }
            else
            {
                ret = new string[expression.Split('.').Length];
                //the start which will move ahead
                int start = 0;
                int mlength = 0;
                char temp;
                int currentRetIndex = 0;
                charStack cse = new charStack(expression.Length);

                for (int i = 0; i < expression.Length; ++i)
                {
                    temp = expression[i];
                    if (temp == '.')
                    {
                        if (cse.IsEmpty)
                        {
                            ret[currentRetIndex++] = expression.Substring(start, mlength);
                            mlength = 0;
                            start = i + 1;
                            continue;
                        }
                    }
                    else if (temp == ')')
                    {
                        if (!cse.IsEmpty)
                        {
                            cse.Pop();
                        }
                    }
                    else if (temp == '(')
                    {
                        cse.Push('(');
                    }
                    mlength++;
                    if (i + 1 >= expression.Length)
                    {
                        ret[currentRetIndex++] = expression.Substring(start, mlength);
                    }
                }
                return ret.Take(currentRetIndex).ToArray();
            }
        }

        /// <summary>
        /// return the lastest refrence string from the src string , make sure src contains "$"
        /// </summary>
        /// <param name="src"></param>
        /// <returns></returns>
        private static string GetLastestRefrenceString(string src)
        {
            charStack cse = new charStack(src.Length);
            for (int i = src.Length - 1; i > -1; i--)
            {
                if (src[i] == ')')
                {
                    cse.Push(')');
                }
                else if(src[i] == '(')
                {
                    if (!cse.IsEmpty)
                        cse.Pop();
                }
                else if (src[i] == '$')
                {
                    if (cse.IsEmpty)
                        return src.Substring(i, src.Length - i);
                }
            }
            return src;
        }


        /// <summary>
        /// simply check the expression if curly is matched
        /// </summary>
        /// <param name="statement"></param>
        /// <returns></returns>
        private static bool CheckSetStatement(string statement)
        {
            if (statement != null && statement != string.Empty)
            {
                charStack cse = new charStack(statement.Length);
                for (int i = 0; i < statement.Length; i++)
                {
                    if (statement[i] == '(')
                    {
                        cse.Push('(');
                    }
                    else if (statement[i] == ')')
                    {
                        if (!cse.IsEmpty)
                        {
                            cse.Pop();
                        }
                        else
                            return false;
                    }
                }
                if (!cse.IsEmpty)
                {
                    return false;
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        private static bool CompareParams(string[] types, UmcTypeMethod utm)
        {
            if (utm.Params.Length != types.Length)
                return false;
            string[] mtypes = new string[types.Length];
            for (int i = 0; i < utm.Params.Length; ++i)
            {
                string[] temp = utm.Params[i].Split(' ');
                if(temp.Length != 2)
                    return false;
                else
                    mtypes[i] = temp[0].Trim();
            }  
            for (int j = 0; j < types.Length; ++j)
            {
                if (types[j] != mtypes[j])
                    return false;
            }
            return true;
        }


        private static string GetTypeByVariable(string variable)
        {
            UmcVariable uv;
            UmcReflector.GlobalVariables.TryGetValue(variable,out uv);
            if (uv != null)
                return uv.VariableType;
            contextVariable.TryGetValue(variable, out uv);
            if (uv != null)
                return uv.VariableType;
            return "void";
        }

        private static string GetTypeByMethod(string type, string methodInvoke)
        {
            UmcType ut;
            UmcReflector.UMCTypes.TryGetValue(type,out ut);
            if (ut != null)
            {
                int theFirstLCurlyIndex = methodInvoke.IndexOf('(');
                List<UmcTypeMethod> utml = ut.Methods.FindAll(p =>
                    p.MethodName == methodInvoke.Substring(0, theFirstLCurlyIndex));
                if (utml.Count > 0)
                {
                    ////if only one same sign
                    //if (utml.Count == 1)
                    //    return utml[0].MethodReturnTypeName;
                    ////if there are more than one method has same sign
                    //else
                    //{
                        string parametersString;
                        if (theFirstLCurlyIndex + 2 >= methodInvoke.Length)
                        {
                            parametersString = "";
                            foreach (UmcTypeMethod utm in utml)
                            {
                                if (utm.MethodParam == "")
                                    return utm.MethodReturnTypeName;
                            }
                        }
                        else
                        {
                            parametersString = methodInvoke.Substring(theFirstLCurlyIndex + 1, methodInvoke.Length - theFirstLCurlyIndex - 2);
                            string[] parameters;
                            parameters = parametersString.Split(',');
                            string[] types = new string[parameters.Length];
                            for (int i = 0; i < parameters.Length; ++i)
                            {
                                types[i] = GetExpressionType(parameters[i]);
                            }
                            foreach (UmcTypeMethod utm in utml)
                            {
                                if (CompareParams(types, utm))
                                    return utm.MethodReturnTypeName;
                            }
                        }
                        return "void";
                    //}
                }
                //if no such method sign
                else
                    return "void";
            }
            //if no such type
            else
            {
                return "void";
            }
        }

        private static string GetTypeByProperty(string type, string propertyName)
        {
            UmcType ut;
            UmcReflector.UMCTypes.TryGetValue(type,out ut);
            if (ut != null)
            {
                UmcTypeProperty utp = ut.Properties.Find(p => p.PropertyName == propertyName);
                if (utp != null)
                    return utp.TypeName;
                else
                    return "void";
            }
            else
            {
                return "void";
            }
        }

        public static string GetExpressionType(string expression)
        {
            string exp = GetLastestRefrenceString(expression);
            if (exp.StartsWith("$") && CheckSetStatement(exp))
            {
                string[] typeStack = ExpressionSplit(expression.Substring(1, expression.Length - 1));
                string type = "void";
                if (typeStack.Length > 0)
                {
                    type = GetTypeByVariable(typeStack[0]);
                    if (type == "void")
                        return type;
                }
                for (int i = 1; i < typeStack.Length; i++)
                {
                    //for method
                    if (typeStack[i].Contains("("))
                    {
                        type = GetTypeByMethod(type, typeStack[i]);
                    }
                    //for property
                    else
                    {
                        type = GetTypeByProperty(type, typeStack[i]);
                    }
                    if (type == "void")
                        return type;
                }
                return type;
            }
            else
            {
                string stringPattern = "['\"][^'\"]*['\"]";
                string DIGIT = "[0-9]";
                string intPattern = string.Format(@"[\-]?{0}+", DIGIT);
                string EXPONENT = string.Format(@"[eE][+\-]?{0}+", DIGIT);
                string float1 = string.Format(@"[\-]?{0}+\.{0}+{1}?", DIGIT, EXPONENT);
                string float2 = string.Format(@"[\-]?\.{0}*{1}?", DIGIT, EXPONENT);
                string float3 = string.Format(@"[\-]?{0}+{1}", DIGIT, EXPONENT);
                string floatPattern = string.Format("{0}|{1}|{2}", float1, float2, float3);

                Match m;
                m = Regex.Match(expression, stringPattern);
                if (m != null && m.Value == expression)
                {
                    return "String";
                }
                m = Regex.Match(expression, intPattern);
                if (m != null && m.Value == expression)
                {
                    return "int";
                }
                m = Regex.Match(expression, floatPattern);
                if (m != null && m.Value == expression)
                {
                    return "Double";
                }
                return "void";
            }
        }


        public static string GetExpressionType(string expression , ref string value)
        {
            string exp = GetLastestRefrenceString(expression);
            if (exp.StartsWith("$") && CheckSetStatement(exp))
            {
                string[] typeStack = ExpressionSplit(expression.Substring(1, expression.Length - 1));
                string type = "void";
                if (typeStack.Length > 0)
                {
                    type = GetTypeByVariable(typeStack[0]);
                    if (type == "void")
                        return type;
                }
                for (int i = 1; i < typeStack.Length; i++)
                {
                    //for method
                    if (typeStack[i].Contains("("))
                    {
                        type = GetTypeByMethod(type, typeStack[i]);
                    }
                    //for property
                    else
                    {
                        type = GetTypeByProperty(type, typeStack[i]);
                    }
                    if (type == "void")
                        return type;
                }
                return type;
            }
            else
            {
                string stringPattern = "['\"][^'\"]*['\"]";
                string DIGIT = "[0-9]";
                string intPattern = string.Format(@"[\-]?{0}+", DIGIT);
                string EXPONENT = string.Format(@"[eE][+\-]?{0}+", DIGIT);
                string float1 = string.Format(@"[\-]?{0}+\.{0}+{1}?", DIGIT, EXPONENT);
                string float2 = string.Format(@"[\-]?\.{0}*{1}?", DIGIT, EXPONENT);
                string float3 = string.Format(@"[\-]?{0}+{1}", DIGIT, EXPONENT);
                string floatPattern = string.Format("{0}|{1}|{2}", float1, float2, float3);

                Match m;
                m = Regex.Match(expression, stringPattern);
                if (m != null && m.Value == expression)
                {
                    value = m.Value.Substring(1,m.Value.Length - 2);
                    return "String";
                }
                m = Regex.Match(expression, intPattern);
                if (m != null && m.Value == expression)
                {
                    value = m.Value;
                    return "int";
                }
                m = Regex.Match(expression, floatPattern);
                if (m != null && m.Value == expression)
                {
                    value = m.Value;
                    return "Double";
                }
                return "void";
            }
        }

    }
}
